题解:P15577 [USACO26FEB] Picking Flowers G

题目链接

分析

这个题目比较有意思。首先可以注意到,一个点能被加入当且仅当其在一条通向 d 的最短路上且这条路原先已经覆盖了全部的带花农场。可以用BFS算法来处理,在计算最短路的前提下维护最短路的前驱及覆盖的花田数量。如果你这样写,会发现无法通过样例1。

分析可得,一个点的最短路可能有多条,而上述方案并不能处理这种情况。于是,改为维护最短路所有前驱以及覆盖最多花田数量。这一部分比较好实现。对于一个节点,其所经过的花田数量就是前驱所经过花田数量的最大值 + 这个节点是否为花田。

现在,我们需要处理哪些点可以添加。从那些最短路径覆盖花田数量为 $K$ 开始沿前驱DFS。如何判断走一个前驱的路径可以覆盖所有花田?我们可以根据经过花田数量的转移方法判断是否是从此方向转移而来,进而判断出那些路可以走。这样子,所经过的点就是可以变成花田的节点。具体可以参照代码理解。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int N = 2e5 + 5;
int hd[N], to[N * 2], nxt[N * 2], num = 1;
void add(int u, int v) {
num++;
to[num] = v;
nxt[num] = hd[u];
hd[u] = num;
}
int t, n, m, k, l;
int s[N], d[N];
int dis[N], fs[N];
bool vis[N], ok[N];
bool is[N];
void dfs(int x) {
if (ok[x])
return;
ok[x] = 1;
if (x == 1)
return;
for (int i = hd[x]; i; i = nxt[i]) {
int v = to[i];
if (dis[v] == dis[x] - 1 && fs[v] + is[x] == fs[x])
dfs(v);
}
}
int main() {
// freopen("Flowers.in", "r", stdin);
// freopen("Flowers.out", "w", stdout);
cin >> t;
while (t--) {
cin >> n >> m >> k >> l;
memset(is, 0, sizeof(is));
for (int i = 1; i <= k; i++) {
cin >> s[i];
is[s[i]] = 1;
}
for (int i = 1; i <= l; i++) {
cin >> d[i];
}
memset(hd, 0, sizeof(hd));
num = 1;
for (int i = 1; i <= m; i++) {
int u, v;
cin >> u >> v;
add(u, v);
add(v, u);
}
memset(dis, 0x3f, sizeof(dis));
memset(vis, 0, sizeof(vis));
memset(fs, 0, sizeof(fs));
dis[1] = 0;
queue<int> q;
q.push(1);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = hd[u]; i; i = nxt[i]) {
int v = to[i];
if (dis[u] + 1 < dis[v]) {
dis[v] = dis[u] + 1;
fs[v] = fs[u] + is[v];
q.push(v);
vis[v] = 1;
} else if (dis[u] + 1 == dis[v]) {
fs[v] = max(fs[v], fs[u] + is[v]);
}
}
}
memset(ok, 0, sizeof(ok));
// for (int i = 1; i <= n; i++) {
// cout << dis[i] << " \n"[i == n];
// }
for (int i = 1; i <= l; i++) {
if (fs[d[i]] == k)
dfs(d[i]);
}
for (int i = 2; i <= n; i++) {
if (ok[i])
cout << 1;
else
cout << 0;
}
cout << endl;
}
return 0;
}